﻿using System.Reflection;
using System;
using System.IO;

namespace Honeycomb.Core
{
    /// <summary>
    /// Manages assemblies for an AppDomain.
    /// </summary>
    public sealed class AssemblyManager : IDisposable
    {
        /// <summary>
        /// The AppDomain to manage.
        /// </summary>
        private readonly AppDomain appDomain = AppDomain.CurrentDomain;

        /// <summary>
        /// Constructor.
        /// </summary>
        public AssemblyManager()
        {
            appDomain.AssemblyResolve += AppDomain_AssemblyResolve;
        }

        /// <summary>
        /// Get assembly instance by assembly full name.
        /// </summary>
        /// <param name="fullName">Assembly full name.</param>
        /// <returns>Assembly instance. Null if not found.</returns>
        public Assembly GetAssemblyByName(string fullName)
        {
            // Process all assemblies in AppDomain.
            foreach (Assembly assembly in appDomain.GetAssemblies())
                if (assembly.GetName().FullName == fullName)
                    return assembly;

            // Assembly was not found. Return null.
            return null;
        }

        /// <summary>
        /// Load an assembly into the AppDomain.
        /// </summary>
        /// <param name="assembly">Serialized assembly.</param>
        public void LoadAssembly(byte[] assembly)
        {
            appDomain.Load(assembly);
        }

        /// <summary>
        /// Retrieves the required assembly name to deserialize the task.
        /// </summary>
        /// <param name="serializedTask">Serialized task to inspect.</param>
        /// <returns>Full name of required assembly. Null if all required assemblies are already loaded.</returns>
        public string GetRequiredAssembyName(byte[] serializedTask)
        {
            AssemblyNameSerializationBinder binder = new AssemblyNameSerializationBinder(this, typeof(TaskBase));
            TaskBase.Deserialize(serializedTask, binder);
            return binder.AssemblyName;
        }

        /// <summary>
        /// Get serialized assembly by full name.
        /// </summary>
        /// <param name="fullName">Full assembly name.</param>
        /// <returns>Serialized assembly.</returns>
        public byte[] GetSerializedAssemblyByName(string fullName)
        {
            Assembly assembly = GetAssemblyByName(fullName);
            return assembly != null ? File.ReadAllBytes(assembly.Location) :  null;
        }

        /// <summary>
        /// Dispose.
        /// </summary>
        public void Dispose()
        {
            appDomain.AssemblyResolve -= AppDomain_AssemblyResolve;
        }
       
        /// <summary>
        /// Assembly resolve method.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="args">Arguments.</param>
        /// <returns>Assembly.</returns>
        private Assembly AppDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            return GetAssemblyByName(args.Name);
        }
    }
}
